/*
 * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.mrdjun.uid.starter.worker;

import cn.mrdjun.uid.starter.exception.UidGenerateException;
import cn.mrdjun.uid.starter.worker.dao.WorkerNodeDAO;
import cn.mrdjun.uid.starter.worker.entity.WorkerNodeEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.transaction.annotation.Transactional;

/**
 * Represents an implementation of {@link WorkerIdAssigner},
 * the worker id will be discarded after assigned to the UidGenerator
 * 基于数据库获取 workerId（机器节点ID）
 *
 * @author yutianbao
 */
@Slf4j
@RequiredArgsConstructor
public class DatasourceWorkerIdAssigner extends AbstractWorkerAssigner implements WorkerIdAssigner {
    private final SqlSessionFactory sqlSessionFactory;

    /**
     * Assign worker id base on database.<p>
     * If there is host name & port in the environment, we considered that the node runs in Docker container<br>
     * Otherwise, the node runs on an actual machine.
     *
     * @return assigned worker id
     */
    @Override
    @Transactional(rollbackFor = UidGenerateException.class)
    public long assignWorkerId() {
        // build worker node entity
        WorkerNodeEntity nodeEntity = buildWorkerNode();

        // add worker node for new (ignore the same IP + PORT)
        try {
            WorkerNodeDAO nodeDAO = sqlSessionFactory.openSession().getMapper(WorkerNodeDAO.class);
            doCheck(nodeDAO);
            WorkerNodeEntity node = nodeDAO.getWorkerNodeByHostPort(nodeEntity.getHostName(), nodeEntity.getPort());
            long workerId;
            if (node == null) {
                nodeDAO.addWorkerNode(nodeEntity);
                workerId = nodeEntity.getId();
                log.info("Add worker node:" + nodeEntity);
            } else {
                nodeDAO.updateWorkerNode(nodeEntity);
                log.info("Update worker node:" + nodeEntity);
                workerId = node.getId();
            }
            return workerId;
        } catch (NoSuchBeanDefinitionException e) {
            String s = "No qualifying bean of type 'org.apache.ibatis.session.SqlSessionFactory' available";
            log.error(s);
            throw new UidGenerateException(s);
        }
    }

    // 检查数据库和表
    public void doCheck(WorkerNodeDAO nodeDAO) {
        if (nodeDAO.queryDatabaseExist() == 0 && nodeDAO.createDatabase() > 0) {
            log.info("Not found database 'fun_cloud_base',auto created success");
            if (nodeDAO.createTable() > 0) {
                log.info("Not found table 'tf_ap_worker_node',auto created success");
            }
        } else if (nodeDAO.queryTableExist() == 0 && nodeDAO.createTable() > 0) {
            log.info("Not found table 'tf_ap_worker_node',auto created success");
        }
    }
}
